package rabbitmq

import (
	"encoding/json"
	"log"

	amqp "github.com/rabbitmq/amqp091-go"
)

type RabbitMq struct {
	channel  *amqp.Channel
	Name     string
	exchange string
}

func failOnError(err error, msg string) {
	if err != nil {
		log.Panicf("%s: %s", msg, err)
	}
}

// New 创建一个新的RabbitMq实例，并完成队列的声明。
// 该函数接受一个字符串参数，该参数是RabbitMQ服务器的连接URL。
// 返回值是一个指向RabbitMq结构的指针，该结构封装了RabbitMQ的相关操作。
func New(s string) *RabbitMq {
	// 使用提供的URL连接到RabbitMQ服务器。
	//fmt.Println("dialing " + s)
	conn, err := amqp.Dial(s)
	// 如果连接失败，输出错误信息并结束函数执行。
	failOnError(err, "Failed to connect to RabbitMQ")
	// 不要close  连接，因为NEW执行完后会关闭连接会关闭所有连接，包括当前连接。
	//defer conn.Close()

	// 打开一个新的通道。
	ch, err := conn.Channel()
	// 如果打开通道失败，输出错误信息并结束函数执行。
	failOnError(err, "Failed to open a channel")
	// 不要close  通道，因为NEW执行完后会关闭连接会关闭所有通道，包括当前通道。
	//defer ch.Close()

	// 声明一个队列，参数如下：
	// - name: 队列名称为空，表示创建一个临时队列。
	// - durable: false，表示队列在RabbitMQ重启后不会持久化。
	// - delete when unused: false，表示队列不会在不再使用时自动删除。
	// - exclusive: false，表示队列不是排他的，可以被其他通道使用。
	// - no-wait: false，表示操作需要等待完成。
	// - arguments: nil，表示没有额外的参数。
	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when unused
		false, // exclusive
		false, // no-wait
		nil,   // arguments
	)
	// 如果队列声明失败，输出错误信息并结束函数执行。
	failOnError(err, "Failed to declare a queue")

	// 返回一个新的RabbitMq实例，包含通道和队列名称。
	return &RabbitMq{channel: ch, Name: q.Name}
}

// https://www.rabbitmq.com/docs/exchanges#what-is-an-exchange
func (q *RabbitMq) Bind(exchange string) {
	failOnError(q.channel.QueueBind(q.Name, "", exchange, false, nil), "Failed to bind a queue")
	q.exchange = exchange
}

func (q *RabbitMq) Send(queue string, body interface{}) {
	str, e := json.Marshal(body)
	failOnError(e, "Failed to marshal a message")

	failOnError(q.channel.Publish(
		"",    // exchange
		queue, // routing key
		false, // mandatory
		false, // immediate
		amqp.Publishing{
			ReplyTo: q.Name,
			Body:    []byte(str),
		}),
		"Failed to publish a message")

}

func (q *RabbitMq) Publish(exchange string, body interface{}) {
	str, e := json.Marshal(body)
	failOnError(e, "Failed to marshal a message")

	failOnError(q.channel.Publish(
		exchange, // exchange
		"",       // routing key
		false,    // mandatory
		false,    // immediate
		amqp.Publishing{
			ReplyTo: q.Name,
			Body:    []byte(str),
		}), "Failed to publish a message")

}
func (q *RabbitMq) Consume() <-chan amqp.Delivery {
	msgs, err := q.channel.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")
	return msgs
}

func (q *RabbitMq) Close() {
	q.channel.Close()

}
